{Object_Title_and_Purpose}


CON
        _clkmode = xtal1 + pll16x                                               'Standard clock mode * crystal frequency = 80 MHz
        _xinfreq = 5_000_000


        GreenEncA = 24
        GreenEncB = 25
        GreenSwitch = 28
        SDI         = 20
        CLK         = 21
        LE          = 22
        RedEncA     = 26
        RedEncB     = 27
        RedSwitch   = 29

        LCDRX       = 6

        LCD         = 0
        DEBUG       = 1

          'Modes:
        PTXmode = 1
        PRXmode = 2
        PTXwithRX = 3
        PRXwithAddr = 4
        Off     = 0
        Disable = 255

        PTXledMask  = |< 16
        PRXledMask  = |< 0
        TestGreenLed = |< 31
        TestRedLed  = |< 15

        TXStatus    = %00000001
        RXStatus    = %00000010

        Maximum = 200 ' Integral value for Switch Debouncing.      
        
VAR
  long  nextLEDbuffer
  long  currLEDbuffer

  byte ModuleMode

  long Stack1[200]

  
  long interval1
  long interval2
  long interval3
  long interval4
  long startTime1
  long startTime2
  long startTime3
  long startTime4

  byte RadioStatus
  byte stateTX, stateRX

  ' Switch Debounce Variables     
  byte buttonState
  long integ1, integ2
  byte output1, output2
  byte pstate1, pstate2

  ' Encoder array variable.
  long pos[4]

  byte encMin1, encMax1, encVal1, encStepSize1
  byte encMin2, encMax2, encVal2, encStepSize2

  byte tempAddr[3]
  byte prevEnc
  byte runState, menuState, subMenuState
  byte updateLCD

   
OBJ
  'Nor      : "Nordic Object 5.29.12"
  Clock    : "Clock"
  fds      : "fullDuplexSerial4port"
  dio      : "dataIO4port"
  SPI      : "SPI_spin"
  'Debug    : "FullDuplexSerial"
  Encoder  : "Quadrature Encoder"
  Numbers  : "Numbers.spin"
  
PUB main | localIndex, temp
 
  Initialize
  
  {Initialize LCD Screen}

  Clock.PauseMSec(1000)
  fds.str(LCD, String(124, 140)) 'Set Backlight to 40%
  fds.str(LCD, String(124, $01)) ' Clear Screen
  fds.str(LCD, String("SynControl v0.01", " Booting Up...  "))

  Clock.PauseMSec(1000)


  cognew(run, @Stack1)
    

  menuState := 0


  repeat

    Clock.MarkSync
      
    
    CASE menuState
      0:
        
      1:
        updateLCD := 0
        setRxAddrMenu  





       
    Clock.WaitSyncMSec(1)

  



    
PUB setRxAddrMenu | localIndex, temp

  CASE updateLCD
    0:

       
      encVal1 := prevEnc := 0
      setMenuEncoder(1, 0, 5, encVal1)
       
      repeat localIndex from 0 to 2
        tempAddr[localIndex] := byte[@rxAddress][localIndex] 'Copy current rxAddress to tempAddr
        dio.hex(LCD, byte[@tempAddr][localIndex], 2)         'print current rxAddress
        
       ' Turn on underline blinking cursor.
      fds.str(LCD, String($FE, $0E))
     
      updateLCD := 1
     
    2:
      if encVal1 <> prevEnc
        if prevEnc // 2
          tempAddr[prevEnc] &= $F0
          tempAddr[prevEnc] &= encVal2
        else
          tempAddr[prevEnc] &= $0F
          tempAddr[prevEnc] &= encVal2 << 4
          
        temp := byte[@tempAddr][encVal1/2]
        if encVal1 // 2
          temp >>= 4
        temp &= $0F
        setMenuEncoder(2, 0, 15, temp)
       
      fds.tx(LCD, $FE)
      fds.tx(LCD, $CB+encVal1)    
      if encVal1 // 2 
        dio.hex(LCD, (byte[@tempAddr][encVal1 / 2] & $0F), 1)
      else
        dio.hex(LCD, byte[@tempAddr][encVal1 / 2] & $F0 >> 4, 1)
       
       
      encVal1 += Encoder.ReadDelta(0)
      encVal2 += Encoder.ReadDelta(1)
       
      prevEnc := encVal1
    
PUB Initialize



  ' Set up LCD Display
  fds.Init
  fds.AddPort(LCD,-1,LCDRX,-1,-1,0,0,9600)
  fds.AddPort(DEBUG, 31, 30, -1, -1, 0, 0, 57600)
  fds.Start
  
  'Debug.start(31, 30, 0, 57600)

  Clock.PauseMSec(1000)
  fds.str(LCD, String($7C, 140)) 'Set Backlight to 40%
  fds.str(LCD, String(254, $01)) ' Clear Screen
  Clock.PauseMSec(1000)
  fds.str(LCD, String("Attempt Addr Chg"))
  
  ' Set up Parallax Serial Terminal for debugging.
  fds.tx(DEBUG, 16) ' Clear Screen

  ' Initialiaze variables for the various run timers.
  interval1 := clkfreq
  interval2 := clkfreq/2
  interval3 := clkfreq/20
  interval4 := clkfreq/20
  startTime1 := cnt
  startTime2 := cnt




PUB run

  ' Make Switch Pins input
  dira[GreenSwitch]~ 
  dira[RedSwitch]~

  buttonState := %00
  integ1 := 0
  integ2 := 0
  output1 := 0
  output2 := 0
  pstate1 := 0
  pstate2 := 0
  
  ' Set up SPI LED Driver
  nextLEDbuffer := currLEDbuffer :=  %0
  SPI.start(15, 0) ' Start the SPI driver for the LED Display
  outa[LE]~
  dira[LE]~~
  SPI.SHIFTOUT(SDI, CLK, 4, 32, currLEDbuffer)
  waitcnt(clkfreq/1000 + cnt)
  outa[LE]~~
  outa[LE]~

  ' Set up encoders
  Encoder.start(GreenEncA, 2, 2, @Pos)
 
  repeat
    CheckButtons
    CheckState ' This will set press/release flags
    updateMenuEncoders
    updateModeLeds
    updateLEDbuffer
    
    
PUB setMenuEncoder(encNum, minVal, maxVal, default)

  if encNum == 1
    encMin1 := minVal
    encMax1 := maxVal
    encVal1 := default
  elseif encNum == 2
    encMin2 := minVal
    encMax2 := maxVal
    encVal2 := default

PRI updateMenuEncoders | temp

  temp := Encoder.ReadDelta(0)
  encVal1 += temp
  encVal1 #>= encMin1
  encVal1 <#= encMax1

  temp := Encoder.ReadDelta(1)
  encVal2 += temp
  encVal2 #>= encMin2
  encVal2 <#= encMax2
    
PRI CheckButtons

    if ina[GreenSwitch] == 1
      if integ1 > 0
        integ1--
    elseif integ1 < Maximum
      integ1++
       
    if integ1 == 0
      output1 := 0
    elseif integ1 => Maximum
      output1 := 1
      integ1 := Maximum

    if ina[RedSwitch] == 1
      if integ2 > 0
        integ2--
    elseif integ2 < Maximum
      integ2++

    if integ2 == 0
      output2 := 0
    elseif integ2 => Maximum
      output2 := 1
      integ2 := Maximum

PRI CheckState

  CASE pstate1
    0:    
      if output1 == 1
        pstate1 := 1
    1:
      'Do Nothing
    2:
      if output1 == 0
        pstate1 := 3
    3:
      'Do Nothing
    Other:
      pstate1 := 0
      
  CASE pstate2
    0:    
      if output2 == 1
        pstate2 := 1
    1:
      'Do Nothing
    2:
      if output2 == 0
        pstate2 := 3
    3:
      'Do Nothing
    Other:
      pstate2 := 0    

PRI stateToMask
  result := 0
  if(buttonState&%01 == %01)
    result |= 1<<RedSwitch
  if(buttonState&10 == %10)
    result |= 1<<GreenSwitch

PRI updateLEDbuffer
 
  if nextLEDbuffer <> currLEDbuffer
    SPI.SHIFTOUT(SDI, CLK, 4, 32, currLEDbuffer := nextLEDbuffer)
    waitcnt(clkfreq/1000 + cnt)
    outa[LE]~~
    outa[LE]~   
    
PRI setLCDCustomChar(CharNumber, MemPosition) | localIndex
  fds.tx(LCD, MemPosition + $40)
  repeat localIndex from 0 to 7
    fds.tx(LCD, byte[@LeftArrow+CharNumber][localIndex])

PRI updateModeLEDs

  if cnt - startTime1 > interval1
    ModuleMode := 0'Nor.getModuleMode
    startTime1 += interval1

  if cnt - startTime2 > interval2
    startTime2 += interval2
    CASE ModuleMode
      PTXwithRX:
        nextLEDbuffer |= PTXledMask   'Turn on PTX LED
        nextLEDbuffer &= !PRXledMask  'Turn off PRX LED
      PTXmode:
        nextLEDbuffer |= PTXledMask   'Turn on PTX LED
        nextLEDbuffer ^= PRXledMask   'Toggle PRX Led
      PRXwithAddr:
        nextLEDbuffer &= !PTXledMask  'Turn off PTX LED
        nextLEDbuffer |= PRXledMask   'Turn on PRX LED
      PRXmode:
        nextLEDbuffer |= PRXledMask   'Turn on PRX LED
        nextLEDbuffer ^= PTXledMask   'Toggle PTX LED
      Off:
        nextLEDbuffer &= !PTXledMask  'Turn off PTX LED
        nextLEDbuffer &= !PRXledMask  'Turn off PTX LED
        ModuleMode := Disable
      Disable:          
      OTHER:

    
    
  'RadioStatus := Nor.getRadioStatus
  
  CASE stateTX
    0:
      if RadioStatus & TXStatus
        stateTX := 1
        startTime3 := cnt
    1:
      nextLEDbuffer ^= PTXledMask
      stateTX := 2
    2:
      if cnt - startTime3 > interval3
        startTime3 += interval3
        nextLEDbuffer ^= PTXledMask
        stateTX := 3
    3:
      if cnt - startTime3 > interval3
        if RadioStatus & TXStatus
          stateTX := 1
        else
          stateTX := 0
    OTHER:
      stateTX := 0 
    
  CASE stateRX
    0:
      if RadioStatus & RXStatus
        stateRX := 1
        startTime4 := cnt
    1:
      nextLEDbuffer ^= PRXledMask
      stateRX := 2
    2:
      if cnt - startTime4 > interval4
        startTime4 += interval4
        nextLEDbuffer ^= PRXledMask
        stateRX := 3
    3:
      if cnt - startTime4 > interval4
        if RadioStatus & RXStatus
          stateRX := 1
        else
          stateRX := 0
    OTHER:
      stateRX := 0

        

 

PRI setModeLED
  



DAT

StringRequest    byte  "Request: ", 0
TestData         byte  "SynControl v0.01", 0
TestAddress      byte  $A00001
LeftArrow        byte  $02,$06,$0F,$1F,$0F,$06,$02,$00
Dash             byte  $00,$00,$1F,$1F,$1F,$00,$00,$00
RightArrow       byte  $08,$0C,$1E,$1F,$1E,$0C,$08,$00
BrokenDash       byte  $00,$00,$11,$11,$11,$00,$00,$00

rxAddress        byte  $B0,$00,$01       